﻿using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace System
{
    /// <summary>
    /// This static class is used to encryption.
    /// Before using this class, don't forget to set a private key as 'private const Password'
    /// </summary>
    public static partial class Utility
    {
        #region [MEMBERS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        private const string Password = "Any Private Key";

        #endregion

        #region [PUBLIC METHODS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        /// <summary>
        /// Used to encrypt any given text
        /// </summary>
        /// <param name="clearText">Text to be encrypt</param>
        /// <returns>string</returns>
        public static string Encrypt(this string pClearText)
        {
            if (string.IsNullOrEmpty(pClearText)) return string.Empty;

            string encryptedText = string.Empty;
            string retValue = string.Empty;
            try
            {
                byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(pClearText);
                PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
                byte[] encryptedData = Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16));
                encryptedText = Convert.ToBase64String(encryptedData);
                retValue = encryptedText.Replace("+", "*");
                retValue = retValue.Replace("/", "|");
                return retValue;
            }
            catch (Exception exc)
            {
                string mesaj = string.Format(@"GelenDeger = '{0}'
                                 \r\n| SifreliDeger = '{1}'
                                 \r\n| DonenDeger = '{2}'", pClearText, encryptedText, retValue);

                exc.Data.Add("mesaj", mesaj);
                exc.Data.Add("method", "Encrypt");
                throw exc;
            }
        }

        /// <summary>
        /// Used to decrypt any encrypted text.
        /// </summary>
        /// <param name="cipherText">Text to be decrypt</param>
        /// <returns>string</returns>
        public static string Decrypt(this string pCipherText)
        {
            if (string.IsNullOrEmpty(pCipherText)) return string.Empty;

            string encryptedText = string.Empty;
            string retValue = string.Empty;
            try
            {
                encryptedText = pCipherText.Replace("*", "+");
                encryptedText = encryptedText.Replace("|", "/");
                byte[] cipherBytes = Convert.FromBase64String(encryptedText);
                PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
                byte[] decryptedData = Decrypt(cipherBytes, pdb.GetBytes(32), pdb.GetBytes(16));
                retValue = System.Text.Encoding.Unicode.GetString(decryptedData);
                return retValue;
            }
            catch (Exception exc)
            {
                string mesaj = string.Format(@"GelenDeger = '{0}'
                                 \r\n| OrjinalSifreliDeger = '{1}'
                                 \r\n| DonenDeger = '{2}'", pCipherText, encryptedText, retValue);

                exc.Data.Add("mesaj", mesaj);
                exc.Data.Add("method", "Decrypt");
                throw exc;
            }
        }

        #endregion

        #region [PRIVATE METHODS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        private static byte[] Encrypt(byte[] clearData)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            return Encrypt(clearData, pdb.GetBytes(32), pdb.GetBytes(16));
        }
        private static byte[] Encrypt(byte[] clearData, byte[] Key, byte[] IV)
        {
            MemoryStream ms = new MemoryStream();
            Rijndael alg = Rijndael.Create();
            alg.Key = Key;
            alg.IV = IV;
            CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(clearData, 0, clearData.Length);
            cs.Close();
            byte[] encryptedData = ms.ToArray();
            return encryptedData;
        }

        private static byte[] Decrypt(byte[] cipherData)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            return Decrypt(cipherData, pdb.GetBytes(32), pdb.GetBytes(16));
        }
        private static byte[] Decrypt(byte[] cipherData, byte[] Key, byte[] IV)
        {
            MemoryStream ms = new MemoryStream();
            Rijndael alg = Rijndael.Create();
            alg.Key = Key;
            alg.IV = IV;
            CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
            cs.Write(cipherData, 0, cipherData.Length);
            cs.Close();
            byte[] decryptedData = ms.ToArray();
            return decryptedData;
        }

        #endregion

        #region [MD5 ENCRYPTION] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        /// <summary>
        /// Hash an input string and return the hash as
        /// a 32 character hexadecimal string.</summary>
        public static string GetMd5Hash(this string pInput)
        {
            // Create a new instance of the MD5CryptoServiceProvider object.
            MD5 md5Hasher = MD5.Create();

            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(pInput));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }

        /// <summary>
        /// Verify a hash against a string.
        /// </summary>
        public static bool VerifyMd5Hash(this string pInput, string pHashedValue)
        {
            // Hash the input.
            string hashOfInput = GetMd5Hash(pInput);

            // Create a StringComparer an compare the hashes.
            StringComparer comparer = StringComparer.OrdinalIgnoreCase;

            if (0 == comparer.Compare(hashOfInput, pHashedValue))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        
        #endregion
    }
}
